import os, sys, subprocess, re, time, signal;
from subprocess import Popen, PIPE, STDOUT;
   

from config import *    # loads global variables and helper function from default_vars.py

#
#    PROJECT 1B - GRADING APPLICATION - Helper Function
#
#    (this file contains functions used by main.py with allow the main
#        script to be more readable)


# returns all directories in 'directory' whose name is 9 digits (i.e. SID number)
def listdirs(folder):
	dirs = []
	for d in (os.listdir(folder)):
        # only return directories that have a 9-digit name
		if (os.path.isdir(os.path.join(folder, d)) and re.match("^[0-9]{9}$",d)):
			dirs.append(d)
	return dirs

# returns a readable time format
# PARAMETER:	seconds	=> time in seconds
# RETURNS:	time as a string in hours, minutes, and seconds
def GetInHMS(seconds):
	hours = seconds / 3600
	seconds -= 3600*hours
	minutes = seconds / 60
	seconds -= 60*minutes
	if hours == 0:
		if minutes == 0:
			return "%02d secs" % (seconds)
		else:
			return "%02d mins %02d secs" % (minutes, seconds)
			

# construct string for MyUCLA Gradebook TSV file entry and write to file
# format: sid <tab> name <tab> score <tab> [comment]
def write_gradebook(fd, sid, pt_array, comment,php_flag):
	try:
		out_str = str(sid)
		total_1 = pt_array[0] + pt_array[1]
		total_2 = pt_array[2] + pt_array[3] + pt_array[4] + pt_array[5]
		if(total_2 < 0):
			total_2 = 0   	
		if(php_flag == 1):
			for i in range(0,len(php_test_query)):
				   total_1 += pt_array[6+i] 				
		total = total_1 + total_2
		
		for idx in range(0,len(pt_array)):
			out_str += "\t" + str(pt_array[idx])
		out_str += "\t" + str(total) + "\t" + str(comment) + "\n"  
		fd.write(out_str)
		fd.flush()
	except OSError, e:
		print >>sys.stderr, "Unable to store gradebook entry for SID: " + sid
		exit()
	return 0

# given a set of expected results and a set of student's results
# assign score (0 to 1.0) based on how many results are matching
def grade(grader_results, student_results, q_num):	
	if (len(grader_results) == 0):
		print "ERROR: Unexpected Input - Grader's query output file has length 0"
		return -1
                                                                                               
	if(len(grader_results ^ student_results) > 0):
		score = 0
		print "Query ", q_num, "\nANSWER:\n" , '\n'.join(grader_results), " \nSTUDENT:\n", '\n'.join(student_results)	
	else:
		score = 1                                                                                                
	return score

# SPLIT UP THE QUERIES IN THE STUDENT'S SUBMITTED FILE
# (use semi-colon as separator)
def extract_queries(students_all_queries_file, num):
	error = 1
	all_student_queries = open(students_all_queries_file, 'r').read()
	if(all_student_queries.find(';') > 0):
		error = 0
	all_queries = all_student_queries.split(';')
	queries = []
	processed_queries = 0
	for q in all_queries:
		# process the first num queries only
		if (processed_queries >= num):
			break
		# process non-empty and select queries only
		if ( len(q.strip()) != 0 and q.find("SELECT") >= 0):
			queries.append(q)
			processed_queries += 1
	return queries,error

# RUN GIVEN GRADER'S QUERY ON STUDENT'S DATABASE AND COMPARE RESULTS TO GRADER'S RESULTS
def run_query(query, grader_result, num, timeout):
	fail_query, score, toprint = (0,0,0)
	(timeout_error, stdout, stderr, retcode) = runCmd(query, timeout)

	if (timeout_error):
		return timeout_error, score, retcode

	#returncode 0 if no error, 1 if error, less than 0 if terminated by signal
	try:
		if retcode == 0:
		    fail_query = 0
		    student_result = stdout.splitlines()
		    
		    # remove empty entries
		    for line in student_result:
			    if not line.strip():
				    student_result.remove(line)

		    # remove field names line
		    if (len(student_result) != 0):
	    		student_result.pop(0)

		    score = grade(grader_result, set(student_result), num)

		else: 
		    fail_query = 1
		    if retcode < 0:
        		print "ERROR: Child was terminated by signal", -retcode
		    else:
				print "ERROR: " + str(retcode) + ":" + stderr + " when executing command: " + query  

	except OSError, e:
		print >>sys.stderr, "ERROR: Execution failed:", e
	return fail_query, score, retcode
	

# Run Command with a Timeout value
def runCmd(cmd, timeout):
	'''
	Will execute a command, read the output and return it back.

	@param cmd: command to execute
	@param timeout: process timeout in seconds
	@return: a tuple of three: first stdout, then stderr, then exit code
	@raise OSError: on missing command or if a timeout was reached
	'''

	ph_out = None # process output
	ph_err = None # stderr
	ph_ret = None # return code

	p = subprocess.Popen(cmd, shell=True,
                	     stdout=subprocess.PIPE,
                	     stderr=subprocess.PIPE,
			     bufsize=0)
	# if timeout is not set wait for process to complete
	if not timeout:
	    ph_ret = p.wait()
	else:
	    fin_time = time.time() + timeout
	    while p.poll() == None and fin_time > time.time():
        	time.sleep(1)

	    # if timeout reached, raise an exception
		if fin_time < time.time():
			return (1, 0, 'TIME OUT', 1)	# return timeout error

	ph_ret = p.returncode
	ph_out, ph_err = p.communicate()

	return (0, ph_out, ph_err, ph_ret)

